use util::RawData;

use crate::destor_acquire;

extern "C" {
    pub fn super_feature(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn super_feature_smooth(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn super_feature_avx(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn super_feature_avx_fast(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn Finesse(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn Finesse_avx(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn odess(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn zok(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn odess_smooth(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn sisd_rabin_fixed(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn sisd_rabin_fixed_avx(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature);
    pub fn gear_roll_hash(p:*const libc::c_uchar,n:libc::c_int,sf:*mut SuperFeature) -> libc::c_int;
    pub fn test_pack_feature(sf:*mut SuperFeature);
}
#[derive(Clone, Copy, Debug)]
pub enum SuperFeatureMethod {
    SuperFeature,
    SuperFeatureAvx,
    SuperFeatureAvxFast,
    SuperFeatureSmooth,
    Finesse,
    FinesseAvx,
    Odess,
    Zok,
    OdessSmooth,
    SisdRabinFixed,
    SisdRabinFixedAvx,
}

impl SuperFeatureMethod {
    pub fn is_smooth(&self) -> bool {
        match self {
            Self::SuperFeatureSmooth => true,
            Self::OdessSmooth => true,
            _ => false,
        }
    }
}

pub static mut SUPER_FEATURE_METHOD:SuperFeatureMethod = SuperFeatureMethod::SuperFeature;
pub fn super_feature_algorithm_acquire() -> SuperFeatureMethod {
    unsafe { SUPER_FEATURE_METHOD }
}

pub fn config_super_feature_algorithm(super_feature_method:&str) -> Result<(),String> {
    unsafe {
        SUPER_FEATURE_METHOD = match super_feature_method {
            "superfeature" => SuperFeatureMethod::SuperFeature,
            "superfeature-smooth" => SuperFeatureMethod::SuperFeatureSmooth,
            "superfeature-avx" => SuperFeatureMethod::SuperFeatureAvx,
            "superfeature-avx-fast" => SuperFeatureMethod::SuperFeatureAvxFast,
            "finesse" => SuperFeatureMethod::Finesse,
            "finesse-avx" => SuperFeatureMethod::FinesseAvx,
            "odess" => SuperFeatureMethod::Odess,
            "odess-smooth" => SuperFeatureMethod::OdessSmooth,
            "zok" => SuperFeatureMethod::Zok,
            "sisd-rabin-fixed" => SuperFeatureMethod::SisdRabinFixed,
            "sisd-rabin-fixed-avx" => SuperFeatureMethod::SisdRabinFixedAvx,
            _ => return Err(super_feature_method.to_string()),
        };
        Ok(())
    }
}
#[repr(C)]
#[derive(RawData,PartialEq)]
pub struct SuperFeature {
    feature:[u64;4]
}

impl SuperFeature {
    pub fn new() -> Self {
        Self {feature:[0;4]}
    }

    pub fn init(&mut self,data:&[u8]) {
        match super_feature_algorithm_acquire() {
            SuperFeatureMethod::SuperFeature => self.super_feature_init(data),
            SuperFeatureMethod::SuperFeatureSmooth => self.super_feature_smooth_init(data),
            SuperFeatureMethod::SuperFeatureAvx => self.super_feature_avx_init(data),
            SuperFeatureMethod::SuperFeatureAvxFast => self.super_feature_avx_fast_init(data),
            SuperFeatureMethod::Finesse => self.finesse_init(data),
            SuperFeatureMethod::FinesseAvx => self.finesse_avx_init(data),
            SuperFeatureMethod::Odess => self.odess_init(data),
            SuperFeatureMethod::Zok => self.zok_init(data),
            SuperFeatureMethod::OdessSmooth => self.odess_smooth_init(data),
            SuperFeatureMethod::SisdRabinFixed => self.sisd_rabin_fixed_init(data),
            SuperFeatureMethod::SisdRabinFixedAvx => self.sisd_rabin_fixed_avx_init(data),
        }
    }

    pub fn features(&self,sf_num:usize) -> &[u64] {
        &self.feature[0..sf_num]
    }

    pub fn super_feature_init(&mut self,data:&[u8]) {
        unsafe {
            super_feature(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
            //super_feature_smooth(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
        }
    }

    pub fn super_feature_smooth_init(&mut self,data:&[u8]) {
        unsafe {
            super_feature_smooth(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
        }
    }

    pub fn super_feature_avx_init(&mut self,data:&[u8]) {
        unsafe {
            if data.len() >= 16*48 {
                super_feature_avx(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
            } else {
                super_feature(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self)
            }
        }
    }

    pub fn super_feature_avx_fast_init(&mut self,data:&[u8]) {
        unsafe {
            if data.len() >= 16*48 {
                super_feature_avx_fast(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
            } else {
                super_feature(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self)
            }
        }
    }

    pub fn finesse_init(&mut self,data:&[u8]) {
        unsafe { Finesse(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self); }
    }

    pub fn finesse_avx_init(&mut self,data:&[u8]) {  
        unsafe {
            if data.len() >= 48 * destor_acquire().super_feature_subregion_num() {
                Finesse_avx(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
            } else {
                Finesse(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self)
            }
        }
    }

    pub fn odess_init(&mut self,data:&[u8]) {
        unsafe { odess(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self); }
    }

    pub fn odess_smooth_init(&mut self,data:&[u8]) {
        unsafe { odess_smooth(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self); }
    }

    pub fn zok_init(&mut self,data:&[u8]) {
        unsafe { zok(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self); }
    }

    pub fn sisd_rabin_fixed_init(&mut self,data:&[u8]) {  
        unsafe { sisd_rabin_fixed(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self); }
    }

    pub fn sisd_rabin_fixed_avx_init(&mut self,data:&[u8]) {  
        unsafe {
            if data.len() >= 48 * destor_acquire().super_feature_subregion_num() {
                sisd_rabin_fixed_avx(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self);
            } else {
                sisd_rabin_fixed(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self)
            }
        }
    }

    pub fn test_gear_roll(&mut self,data:&[u8]) -> usize {
        unsafe {
            gear_roll_hash(&data[0] as *const libc::c_uchar, data.len().try_into().unwrap(), self) as usize
        }
    }

    pub fn test_pack_feature(&mut self) {
        unsafe {
            test_pack_feature(self)
        }
    }
}